package com.demo.test.leak

import android.app.Activity
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.TextView
import com.fortest.R
import com.demo.Tags
import com.demo.component.LeakingThread1

/**
泄露方式：Thread的本地变量
泄露原因：LeakingThread这个线程一旦执行run方法，马上就把自己挂起来了，等待notify()或者notifyAll()，然而没有其它线程来唤醒它，因此它会一直活着，所持有的资源也不会释放
在Activity中把一个view放到Thread的List中，使得这个view会一直被Thread长期持有，
因此即使Activity已经退出了，Activity的view仍然被Thread所引用着，而这个view得不到释放。

泄露报告　－－　StrictMode：
2020-05-24 21:17:19.546 13280-13280/? E/StrictMode: class com.demo.test.leak.ActivityLeakG_Thread1; instances=2; limit=1
android.os.StrictMode$InstanceCountViolation: class com.demo.test.leak.ActivityLeakG_Thread1; instances=2; limit=1
at android.os.StrictMode.setClassInstanceLimit(StrictMode.java:1)

泄露报告　－－　LeakCanary：
23596 bytes retained by leaking objects
Signature: c7d6c11fbb93566e628530db27885d0a5a2de
┬───
│ GC Root: Local variable in native code
│
├─ com.demo.component.LeakingThread instance
│    Leaking: UNKNOWN
│    Thread name: 'Leaking thread'
│    ↓ LeakingThread.leakedViews
│                    ~~~~~~~~~~~
├─ java.util.ArrayList instance
│    Leaking: UNKNOWN
│    ↓ ArrayList.elementData
│                ~~~~~~~~~~~
├─ java.lang.Object[] array
│    Leaking: UNKNOWN
│    ↓ Object[].[0]
│               ~~~
├─ android.widget.TextView instance
│    Leaking: YES (View.mContext references a destroyed activity)
│    mContext instance of com.demo.test.leak.ActivityLeakG_Thread1 with mDestroyed = true
│    View#mParent is set
│    View#mAttachInfo is null (view detached)
│    View.mID = R.id.helper_text
│    View.mWindowAttachCount = 1
│    ↓ TextView.mContext
╰→ com.demo.test.leak.ActivityLeakG_Thread1 instance
​     Leaking: YES (ObjectWatcher was watching this because com.demo.test.leak.ActivityLeakG_Thread1 received Activity#onDestroy() callback and Activity#mDestroyed is true)
​     key = 2956ef5e-fcd6-4fa6-b06f-3ad53b3be831
​     watchDurationMillis = 116817
​     retainedDurationMillis = 111812
 **/
class ActivityLeakG_Thread1 : Activity() {
    val TAG = Tags.leakThread1
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_leak_common)
        val textView = findViewById<TextView>(R.id.helper_text)
        textView.text = "泄露方式：Thread的本地变量\n" +
                "泄露原因：LeakingThread这个线程一旦执行run方法，马上就把自己挂起来了，等待notify()或者notifyAll()，然而没有其它线程来唤醒它，因此它会一直活着，所持有的资源也不会释放\n" +
                "在Activity中把一个view放到Thread的List中，使得这个view会一直被Thread长期持有，\n" +
                "因此即使Activity已经退出了，Activity的view仍然被Thread所引用着，而这个view得不到释放。"


        val leakedView = findViewById<View>(R.id.helper_text)

        Log.d(TAG, "线程的fields，导致内存泄露")
        // Leak from thread fields
        LeakingThread1.thread.leakedViews.add(leakedView)
    }
}
